//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#pragma once

#include "UI/Types/UIMaterial.h"

namespace Engine
{
namespace UI
{

	//
	// UI Image
	//

	struct UIImage
	{
		friend struct ::MetaData::UIImageCfg;

	// types
	public:

		//
		// Frame Indices
		//
		struct FrameIndices
		{
		// types
		public:
			typedef uint8										index_t;
			typedef static_array< index_t, ColorStyle::_COUNT >	index_array_t;


		// variables
		private:
			index_array_t	_indices;


		// methods
		public:
			FrameIndices () { ZeroMem( _indices.ptr(), _indices.Count() ); }

			static uint	Count ()					{ return index_array_t::STATIC_COUNT; }

			index_t		operator [] (uint i) const	{ return _indices[ i ]; }
			index_t &	operator [] (uint i)		{ return _indices[ i ]; }

			index_t		Idle ()				const	{ return _indices[ ColorStyle::IDLE ]; }
			index_t &	Idle ()						{ return _indices[ ColorStyle::IDLE ]; }

			index_t		Disabled ()			const	{ return _indices[ ColorStyle::DISABLED ]; }
			index_t &	Disabled ()					{ return _indices[ ColorStyle::DISABLED ]; }

			index_t		IdlePressed ()		const	{ return _indices[ ColorStyle::IDLE_PRESSED ]; }
			index_t &	IdlePressed ()				{ return _indices[ ColorStyle::IDLE_PRESSED ]; }

			index_t		Checked ()			const	{ return _indices[ ColorStyle::CHECKED ]; }
			index_t &	Checked ()					{ return _indices[ ColorStyle::CHECKED ]; }

			index_t		CheckedPressed ()	const	{ return _indices[ ColorStyle::CHECKED_PRESSED ]; }
			index_t &	CheckedPressed ()			{ return _indices[ ColorStyle::CHECKED_PRESSED ]; }
		};


	private:
		typedef static_array< rect, 9 >						texcoords_t;		// 9 path coords
		typedef static_array< rect, ColorStyle::_COUNT >	frame_coords_t;		// texture coords for frame


	// variables
	private:
		UITexture		_texture;
		UIMaterial		_material;
		ColorStyle		_colors;
		FrameIndices	_frameIndices;
		irect			_fixedBorder;
		uvec2			_numFrames;
		uvec2			_borderSize;
		vec2			_frameSize;
		texcoords_t		_texcoords;
		frame_coords_t	_frameCoords;
		uint8			_numTexcoords;
		bool			_proportional;


	// methods
	public:
		UIImage () : _numTexcoords(0)
		{}

		void SetTexture (const UITexture &tex)		{ _texture = tex; }
		void SetMaterial (const UIMaterial &mtr)	{ _material = mtr; }
		void SetColorStyle (const ColorStyle &cs)	{ _colors = cs; }


		vec2 FrameSize ()		const		{ return _frameSize; }
		//vec2 Size ()			const		{ return _texture.Dimension().To<vec2>(); }
		bool HasTexture ()		const		{ return _texture.IsLoaded(); }
		bool IsTransparent ()	const		{ return _colors[ ColorStyle::IDLE ].A() == 0; }
		bool IsDefined ()		const		{ return HasTexture() and not IsTransparent(); }
		bool IsValid ()			const		{ return _material.IsValid(); }
		bool IsProportional ()	const		{ return _proportional; }


		void Draw (const Graphics::BatchRenderPtr &br, const rect &region, ColorStyle::EType state) const
		{
			if ( HasTexture() )
				DrawTextured( br, region, _frameCoords[ state ], _colors[ state ] );
			else
				DrawColored( br, region, _colors[ state ] );
		}


		void DrawColored (const Graphics::BatchRenderPtr &br, const rect &region, const color4u &color) const
		{
			br->AddBatch( _material, Primitives::Rect2D( region, rect(), color ) );
		}


		void DrawTextured (const Graphics::BatchRenderPtr &br, const rect &region, const rect &frameTexc, const color4u &color) const
		{
			for (uint i = 0; i < _numTexcoords; ++i) {
				br->AddBatch( _material, _BuildPrimitive( region, frameTexc, _texcoords[i], color ) );
			}
		}


		void OnLoad ()
		{
			_material.SetTexture( 0, _texture.Texture() );
			
			// build frames
			const bool	has_frames =  All( _numFrames.IsNotZero() );

			if ( has_frames )
				_frameSize = ( _texture.Dimension() / _numFrames - _borderSize*2 ).To<vec2>();
			else
				_frameSize = _texture.Dimension().To<vec2>();


			FOR( j, _frameCoords )
			{
				if ( not has_frames or _frameIndices[j] == FrameIndices::index_t(-1) )
				{
					_frameCoords[j] = rect( 0.0f, 0.0f, 1.0f, 1.0f );
					continue;
				}

				UXMathExt::GetTextureFrame( _frameCoords[j], _frameIndices[j], _texture.Dimension(), _numFrames, _borderSize );
			}


			// setup 9 path
			uint		i = 0;
			const rect	tc		= _texture.TexCoords();
			const rect	fb_norm	= _fixedBorder.To<rect>() / _texture.Dimension().To<vec2>();
			const rect	fb		= rect( tc.LeftBottom() + fb_norm.LeftBottom(),
										tc.RightTop() - fb_norm.RightTop() );

			if ( IsZero( _fixedBorder ) )
			{
				_texcoords[i++]	= tc;
				_numTexcoords	= i;
				return;
			}
			
			_texcoords[i++] = fb;
			
			if ( IsNotZero( _fixedBorder.left ) )
			{
				_texcoords[i++] = rect( tc.left, fb.bottom, fb.left, fb.top );
			}

			if ( IsNotZero( _fixedBorder.right ) )
			{
				_texcoords[i++] = rect( fb.right, fb.bottom, tc.right, fb.top );
			}

			if ( IsNotZero( _fixedBorder.bottom ) )
			{
				_texcoords[i++] = rect( fb.left, tc.bottom, fb.right, fb.bottom );
			}
			
			if ( IsNotZero( _fixedBorder.top ) )
			{
				_texcoords[i++] = rect( fb.left, fb.top, fb.right, tc.top );
			}
			

			if ( IsNotZero( _fixedBorder.LeftTop() ) )
			{
				_texcoords[i++] = rect( tc.left, fb.top, fb.left, tc.top );
			}
			
			if ( IsNotZero( _fixedBorder.LeftBottom() ) )
			{
				_texcoords[i++] = rect( tc.LeftBottom(), fb.LeftBottom() );
			}

			if ( IsNotZero( _fixedBorder.RightTop() ) )
			{
				_texcoords[i++] = rect( fb.RightTop(), tc.RightTop() );
			}

			if ( IsNotZero( _fixedBorder.RightBottom() ) )
			{
				_texcoords[i++] = rect( fb.right, tc.bottom, tc.right, fb.bottom );
			}

			_numTexcoords = i;
		}


	private:
		static Primitives::Rect2D  _BuildPrimitive (const rect &region, const rect &texc, const rect &coords, const color4u &color)
		{
			return Primitives::Rect2D(
						rect( region.LeftBottom(), region.LeftBottom() ) + coords * region.GetSize(),
						rect( texc.LeftBottom(), texc.LeftBottom() ) + coords * texc.GetSize(),
						color );
		}
	};


}	// UI
}	// Engine
